package JavaUtils.CSUtils;

import JavaUtils.CSUtils.Log.CLog;
import JavaUtils.weData.We_DataBase;

import java.nio.charset.StandardCharsets;
import java.util.List;

public class BinaryWriter {
    static final String tag = "BinaryWriter";
    int writerIndex;
    byte[] buf;

    public void Write(boolean value) {
        expand(1);
        byte v = value ? (byte) 1 : 0;
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void Write(byte v) {
        expand(1);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void Write(char v) {
        expand(1);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void Write(short v) {
        expand(2);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void WriteUInt16(int v) {
        expand(2);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void Write(int v) {
        expand(4);
        this.buf[this.writerIndex++] = (byte) ((v >>> 24) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 16) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void WriteUInt32(long v) {
        expand(4);
        this.buf[this.writerIndex++] = (byte) ((v >>> 24) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 16) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void Write(float v) {
        Write(Float.floatToIntBits(v));
    }


    public void Write(long v) {
        expand(8);
        this.buf[this.writerIndex++] = (byte) ((v >>> 56) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 48) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 40) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 32) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 24) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 16) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void WriteUInt64(long v) {
        expand(8);
        this.buf[this.writerIndex++] = (byte) ((v >>> 56) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 48) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 40) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 32) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 24) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 16) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v >>> 8) & 0xFF);
        this.buf[this.writerIndex++] = (byte) ((v) & 0xFF);
    }

    public void Write(double v) {
        expand(8);
        Write(Double.doubleToLongBits(v));
    }

    public void Write(String value) {
        short length = 0;
        if (value == null) {
            expand(2);
            Write(length);
            return;
        }
        byte[] sInput = value.getBytes(StandardCharsets.UTF_8);
        if (sInput.length < Short.MAX_VALUE) {
            length = (short) sInput.length;
            expand(length + 2);
            Write(length);
            Write(sInput);
        } else {
            expand(Short.MAX_VALUE + 2);
            Write(Short.MAX_VALUE);
            System.arraycopy(sInput, 0, buf, writerIndex, Short.MAX_VALUE);
        }
    }

    public void Write(byte[] buffer) {
        expand(buffer.length + 2);
        WriteUInt16(buffer.length);
        System.arraycopy(buffer, 0, buf, writerIndex, buffer.length);
        writerIndex += buffer.length;
    }

    public void Write(CInt4 e014_int4) {
        if (e014_int4 == CInt4.zero) {
            Write((byte) 0);
            return;
        }
        Write(e014_int4.o_intLength());
        if (e014_int4.o_intLength() == 1) {
            Write(e014_int4.o_id());
        } else {
            Write(e014_int4.a());
            Write(e014_int4.b());
            Write(e014_int4.c());
            Write(e014_int4.d());
        }
    }

    public <T> void S_WriteByteArrayArray(List<T[]> array, int subType) {
        Write((byte) subType);
        long startPosi = S_WriterByteHead();
        if (array != null) {
            for (T[] obj : array) {
                S_WriteByteArray(obj, subType);
            }
        }
        S_WriterByteEnd(startPosi);
    }

    public void S_WriteByteArray(Object[] array, int subType) {
        Write((byte) subType);
        long startPosi = S_WriterByteHead();
        if (array != null) {
            for (Object obj : array) {
                S_WriteByteObj(obj, subType);
            }
        }
        S_WriterByteEnd(startPosi);
    }

    public void S_WriteByteObj(Object obj, int baseType) {
        switch (baseType) {
            case EBaseType.e001_bool:
                boolean e001_bool = (boolean) (Object) obj;
                Write(e001_bool);
                break;
            case EBaseType.e003_byte:
                byte e002_byte = (byte) (Object) obj;
                Write(e002_byte);
                break;
            case EBaseType.e004_short:
                short e003_short = (short) (Object) obj;
                Write(e003_short);
                break;
            case EBaseType.e005_ushort:
                int e004_ushort = (int) (Object) obj;
                WriteUInt16(e004_ushort);
                break;
            case EBaseType.e006_int:
                int e005_int = (int) (Object) obj;
                Write(e005_int);
                break;
            case EBaseType.e007_uint:
                long e006_uint = (long) (Object) obj;
                WriteUInt32(e006_uint);
                break;
            case EBaseType.e008_float:
                float e007_float = (float) (Object) obj;
                Write(e007_float);
                break;
            case EBaseType.e009_double:
                double e008_double = (double) (Object) obj;
                Write(e008_double);
                break;
            case EBaseType.e010_long:
                long e009_long = (long) (Object) obj;
                Write(e009_long);
                break;
            case EBaseType.e011_ulong:
                long e010_ulong = (long) (Object) obj;
                WriteUInt64(e010_ulong);
                break;
            case EBaseType.e012_string:
                if (obj == null) {
                    WriteUInt16(0);
                    return;
                }
                String e012_string = (String) (Object) obj;
                Write(e012_string);
                break;
            case EBaseType.e013_Byte4:
                if (obj == null) {
                    Write((int) 0);
                    return;
                }
                CInt4 e013_bytes4 = (CInt4) (Object) obj;
                Write(e013_bytes4.o_id());
                break;
            case EBaseType.e014_Int4:
                if (obj == null) {
                    Write((byte) 0);
                    return;
                }
                CInt4 e014_int4 = (CInt4) (Object) obj;
                Write(e014_int4);
                break;
            case EBaseType.e017_Enum:
                short e017_short = (short) ((int) obj);
                Write(e017_short);
                break;
            case EBaseType.e018_array:
            case EBaseType.e019_arrayArray:
            case EBaseType.e026_list:
            case EBaseType.e037_dic:
            case EBaseType.e020_obj:
                CLog.ex(tag, "---数组不能直接调用这个方法4");
                break;
            case EBaseType.e021_weData:
                if (obj == null) {
                    Write((int) 0);
                    return;
                }
                We_DataBase data = (We_DataBase) obj;
                if (data == null) {
                    Write((int) 0);
                } else {
                    Write(data.m_SerializableId);
                }
                break;
        }
    }

    protected void expand(int count) {
        if (count < 0)
            throw new IllegalArgumentException();
        int preLen = writerIndex + count;
        if (buf.length < preLen) {
            preLen = Integer.highestOneBit(preLen);
            preLen <<= 1;
            if (preLen < 0) {
                preLen = Integer.MAX_VALUE;
            }
            byte[] newBuf = new byte[preLen];
            System.arraycopy(buf, 0, newBuf, 0, buf.length);
            buf = newBuf;
        }
    }

    public long S_WriterByteHead() {
        Write((int) 0);
        return writerIndex;
    }

    public void S_WriterByteEnd(long startPosition_) {
        int end = writerIndex;
        int startPosition = (int) startPosition_;
        long dataLength = end - startPosition;
        this.buf[startPosition++] = (byte) ((dataLength >>> 24) & 0xFF);
        this.buf[startPosition++] = (byte) ((dataLength >>> 16) & 0xFF);
        this.buf[startPosition++] = (byte) ((dataLength >>> 8) & 0xFF);
        this.buf[startPosition++] = (byte) ((dataLength) & 0xFF);
    }


    public byte[] S_ToBytes() {
        if (writerIndex < buf.length) {
            byte[] newBuf = new byte[writerIndex];
            System.arraycopy(buf, 0, newBuf, 0, writerIndex);
            return newBuf;
        } else if (writerIndex == buf.length) {
            return buf;
        } else {
            CLog.ex(tag, "错误......writerIndex > buf.length");
            return null;
        }
    }
}
